/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.taglib.core.grid;

import com.blandware.atleap.webapp.struts.ContentTilesRequestProcessor;
import com.blandware.atleap.webapp.taglib.core.grid.util.Grid;
import com.blandware.atleap.webapp.taglib.core.grid.util.SortField;
import com.blandware.atleap.webapp.taglib.core.util.TaglibConstants;
import com.blandware.atleap.webapp.util.core.RequestUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>Tag for marking special area on JSP as grid.
 * <em>Grid</em> is an object that serves to displaying some data and navigating it.
 * Data is represented with <em>rows</em>. Each row corresponds to some object,
 * that can be stored in persistent storage, for example. Some properties of such
 * objects are dedicated and called <em>fields</em>. Each field has it's own name
 * (for example, such names may match names of row columns in database). Fields
 * may be used to filter rows (so grid will display only a subset of all rows) and
 * to sort them.
 * </p>
 * <p>
 * Each field may have a filter assigned to it. Such assignment is made with
 * some filter tag. Currently, four types of filters (and four corresponding tags)
 * are implemented: {@link DateFilterTag}, {@link NumberFilterTag},
 * {@link SetFilterTag} and {@link StringFilterTag}.
 * </p>
 * <p>
 * Grid may be sorted by some fields. This sorting may be simultaneous: first
 * sort is made on one field, then on another and so on (this is similar to
 * SQL statements: there you can specify several ORDER BY clauses like
 * SELECT * FROM user ORDER BY last_name, first_name...). Sort is specified with
 * <em>sort</em> tag, which generates a hyperlink that causes rows to be sorted.
 * </p>
 * <p>
 * Grid also gives ability to split rows by pages. This is done with
 * <em>pager</em>, <em>pagesIterator</em> and other tags.
 * </p>
 * <p>
 * Allowed attributes are:
 * <ul>
 * <li>
 * <b>name</b> - name of grid. If not specified, this will be generated
 * automatically (currently this will be equal to current URI taken from
 * request).
 * </li>
 * <li>
 * <b>pageUrl</b> - URL that corresponds to page on which this grid is
 * displayed. It's used as base for URLs of links that are produced for sorting,
 * paging and so on. If not specified, current page URL will be taken.
 * </li>
 * <li>
 * <b>pageSize</b> - number of elements per page on this grid. If not specified,
 * it's 1000.
 * </li>
 * </ul>
 * </p>
 * <p>
 * For example on using this tag, see package.html in this package.
 * </p>
 * <p><a href="GridTag.java.html"><i>View Source</i></a></p>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.16 $ $Date: 2006/08/03 10:08:19 $
 * @jsp.tag name="grid"
 * body-content="scriptless"
 */
public class GridTag extends SimpleTagSupport {

	protected transient final Log log = LogFactory.getLog(GridTag.class);

	/**
	 * Grid name. This is the key in map of grids that stored in session under
	 * <code>com.blandware.atleap.webapp.taglib.core.util.TaglibConstants.GRIDS'</code> key.
	 * By default request URI is used.
	 *
	 * @see javax.servlet.http.HttpServletRequest#getRequestURI()
	 */
	protected String name;

	/**
	 * Uniform Resource Locator of the action that used to display page
	 * where this grid is located. By default it is URL of current page plus query string if it exists.
	 * URL, if specified, MUST contain protocol and host name.
	 * Optionally it may contain some query string
	 */
	protected String pageUrl;

	/**
	 * Contains all request parameters mapped to their values
	 */
	protected Map parameterMap;

	/**
	 * Number of rows on each page. By default it is 1000
	 */
	protected Integer pageSize = new Integer(1000);

	/**
	 * Current page number
	 */
	protected Integer currentPageNumber = new Integer(1);

	/**
	 * Grid associated with this tag
	 */
	protected Grid grid = null;

	/**
     * Returns grid name
     *
	 * @return grid name
     * @see #name
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Grid name. This is the key in map of grids that stored in session under 'com.blandware.atleap.webapp.taglib.core.util.TaglibConstants.GRIDS' key. By default request URI is used."
	 */
	public String getName() {
		return name;
	}

    /**
     * Sets grid name
     *
     * @param name grid name to set
     * @see #name
     */
	public void setName(String name) {
		this.name = name;
	}

	/**
     * Returns page URL for grid
     *
	 * @return page URL
     * @see #pageUrl
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.String"
	 * description="Uniform Resource Identifier of the action that used to display page where this grid is located"
	 */
	public String getPageUrl() {
		return pageUrl;
	}

    /**
     * Sets page URL for grid
     *
     * @param pageUrl page URL
     * @see #pageUrl
     */
	public void setPageUrl(String pageUrl) {
		this.pageUrl = pageUrl;
	}

    /**
     * Returns map of parameters
     *
     * @return parameter map
     */
	public Map getParameterMap() {
		return parameterMap;
	}

	/**
     * Returns number of items on page
     *
	 * @return page size
     * @see #pageSize
	 * @jsp.attribute required="false"
	 * rtexprvalue="true"
	 * type="java.lang.Integer"
	 * description="Number of rows on each page. By default it is 1000"
	 */
	public Integer getPageSize() {
		return pageSize;
	}

    /**
     * Sets number of items on page
     *
     * @param pageSize page size
     * @see #pageSize
     */
	public void setPageSize(Integer pageSize) {
		this.pageSize = pageSize;
	}

    /**
     * Returns number of current page being displayed
     *
     * @return current page number
     * @see #currentPageNumber
     */
	public Integer getCurrentPageNumber() {
		return currentPageNumber;
	}

    /**
     * Sets number of current page being displayed
     *
     * @param currentPageNumber current page number
     * @see #currentPageNumber
     */
	public void setCurrentPageNumber(Integer currentPageNumber) {
		this.currentPageNumber = currentPageNumber;
	}

    /**
     * Returns Grid object corresponding to this grid
     *
     * @return Grid object
     */
	protected Grid getGrid() {
		return grid;
	}

    /**
     * Processes the tag
     *
     * @throws JspException
     * @throws IOException
     */
	public void doTag() throws JspException, IOException {

		PageContext pageContext = (PageContext) getJspContext();

		// If name atribute is omitted, set name to current request URI

		if ( name == null ) {
			name = ((HttpServletRequest) pageContext.getRequest()).getRequestURI();
		}

		// Get page URL and query string. Convert query string to map

		HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
		String queryString = new String();
		if ( pageUrl == null ) {
			pageUrl = (String) request.getAttribute(ContentTilesRequestProcessor.PROCESSED_URL);
		}
		int k = pageUrl.indexOf('?');
		if ( k != -1 ) {
			queryString = pageUrl.substring(k + 1);
			pageUrl = pageUrl.substring(0, k);
		}

		if ( queryString == null ) {
			queryString = new String();
		}

		parameterMap = RequestUtil.getRequestParametersFromString(queryString);

		// Find grid in session if it was previously created or create new

		HttpSession session = pageContext.getSession();
		Map grids = (Map) session.getAttribute(TaglibConstants.GRIDS);
		if ( grids == null ) {
			grids = Collections.synchronizedMap(new HashMap());
			session.setAttribute(TaglibConstants.GRIDS, grids);
		}


		grid = (Grid) grids.get(this.name);

		// Create grid if it does not exist
		if ( grid == null ) {
			grid = new Grid(this.name);
		}

		grid.setTotal(new Integer(0));

		// Set fields sorting and clear filters if it was specified in request parameters
		if ( request.getParameter("gridName") != null && request.getParameter("gridName").equalsIgnoreCase(this.name) ) {
			if ( request.getParameter("sortField") != null ) {
				String fieldName = request.getParameter("sortField");
				SortField sortField = grid.getSortFieldByFieldName(fieldName);
				if ( sortField != null ) {
					sortField.reverseOrder();
				} else {
					sortField = new SortField(fieldName);
				}
				String rowIteratorsParam = new String();
				if ( request.getParameter("rowIterators") != null ) {
					rowIteratorsParam = request.getParameter("rowIterators");
				}
				String[] rowIterators = rowIteratorsParam.split(",");
				for ( int i = 0; i < rowIterators.length; i++ ) {
					sortField.addRowIterator(rowIterators[i].trim());
				}
				String property = request.getParameter("property");
				if (  property != null && property.trim().length() > 0 ) {
					sortField.setProperty(property);
				}
				grid.addSortField(sortField);
			}

			if ( request.getParameter("pageNumber") != null ) {
				currentPageNumber = new Integer(Integer.parseInt(request.getParameter("pageNumber")));
			}

			if ( request.getParameter("clearAllFilters") != null && request.getParameter("clearAllFilters").equalsIgnoreCase("true") ) {
				grid.clearFilters();
			}

			if ( request.getParameter("clearFilter") != null ) {
				String fieldName = request.getParameter("clearFilter");
				grid.removeFilter(fieldName);
			}
		}

		grid.setPageSize(pageSize);

		JspFragment body = getJspBody();
		if ( body != null ) {
			body.invoke(null);
		}

		// Save grid in session
		grids.put(this.name, grid);
		session.setAttribute(TaglibConstants.GRIDS, grids);
	}

}
